package randomwalk;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import musictrackrecommendation.DatabaseDataset;
import musictrackrecommendation.ExperimentParameters;

/**
 * @author      user
 * @uml.dependency   supplier="randomwalk.socialgraph.SocialGraph"
 * @uml.dependency   supplier="simpledataset.SimpleMatrix"
 */
public abstract class Submatrix {
    /**
	 * @uml.property  name="rowIds"
	 */
    private int[]    rowIds;
    /**
	 * @uml.property  name="columnIds"
	 */
    private int[]    columnIds;
    /**
	 * @uml.property  name="values"
	 */
    private double[] values;
    /**
	 * @uml.property  name="transponedSubmatrixValues"
	 */
    private double[] transponedSubmatrixValues;
    
    /*
     * rowIds - array of Ids associated with rows
     * (for example - user_ids for UserTrack submatrix)
     * columnIds array of Ids associated with columns
     * (for example - track_ids for UserTrack submatrix)
     * values - user preference measure
     * (for example - playcount for UserTrack submatrix)
     */
    
    public Submatrix() throws ClassNotFoundException, SQLException {
        loadFromDatabase();
    }
    
    public Submatrix(int [] rowIds, int[] columnIds, 
    		double[] values, double[] transponedValues) {
        this.setRowIds(rowIds);
        this.setColumnIds(columnIds);
        this.setValues(values);
        this.setTransponedSubmatrixValues(transponedValues);
    }
    
    
    public abstract String getTableName();
    public abstract String getRowFieldName();
    public abstract String getColumnFieldName();
    public abstract String getOrderFieldName();
    public abstract int getSubmatrixSize();
    public abstract double[] getSubmatrixValues(int[] columnIds);


    private void loadFromDatabase() throws ClassNotFoundException, SQLException {
    	ExperimentParameters parameters = ExperimentParameters.getInstance();
		DatabaseDataset datasetDatabase = parameters.getDatasetDatabase();
		
    	Class.forName(datasetDatabase.getJdbcDriverName());

        Connection con =
                DriverManager.getConnection(
                		datasetDatabase.getJdbcConnectionUrl(),
                		datasetDatabase.getDatabaseUser(), 
                		datasetDatabase.getPassword());
        
        Statement stmt = con.createStatement();
        String query = getRowColumnQuery();
        ResultSet rs = stmt.executeQuery(query);
        
        int userTracksCount = getSubmatrixSize();
        
        rowIds = new int[userTracksCount];
        columnIds = new int[userTracksCount];
        values = new double[userTracksCount];
        
        int rowNumber = 0;
        while (rs.next()) {
            rowIds[rowNumber] = rs.getInt(getRowFieldName());
            columnIds[rowNumber] = rs.getInt(getColumnFieldName());
            rowNumber++;
        }
        rs.close();
        con.close();
        
        values = getSubmatrixValues(columnIds);
        //before social graph matrix normalization transponedSubmatrixValues = values
        transponedSubmatrixValues = values.clone();
    }
    
    private String getRowColumnQuery() {
    	String query = "select " + getRowFieldName();
    	query += ", " +  getColumnFieldName();
    	query += " from " + getTableName();
    	query += " order by " + getOrderFieldName();
    	return query;
    }
    
     /**
      * Sparse matrix * vector
      * @param preferenceVector
      * @return matrix*preferenceVector
      */
     public void randomWalkTransition(double[] preferenceVector, 
    		 double[] resultVector, int filteredRow) {
        for(int i = 0; i < getRowIds().length; i++) {
        	if(getRowIds()[i]  != filteredRow){
        		resultVector[getRowIds()[i]] 
            		+= preferenceVector[getColumnIds()[i]] * getValues()[i];
        	}
        }

     }
     /**
      * Transposed sparse matrix * vector
      * @param preferenceVector
      * @return Transposition(matrix)*preferenceVector
      */
     public void randomWalkTransitionTransposition(double[] preferenceVector, 
    		 double[] resultVector) {
        for(int i = 0; i < getRowIds().length; i++) {
            resultVector[getColumnIds()[i]] 
            		+= preferenceVector[getRowIds()[i]] 
            				* getTransponedSubmatrixValues()[i];
        }
     }

     /**
      * @param columnId - id of interesting column
      * @return norm of interesting column
      */
     public double columnNorm(int columnId) {
    	 double norm = 0.0;
    	 for (int i = 0; i < values.length; i ++) {
    		 if (columnIds[i] == columnId)
    			 norm += values[i];
    	 }
    	 return norm;
     }
     
     /**
      * @param columnId - id of interesting column
      * @return norm of interesting column in transponed submatrix
      */
     public double transponedColumnNorm(int columnId) {
    	 double norm = 0.0;
    	 for (int i = 0; i < values.length; i ++) {
    		 if (rowIds[i] == columnId)
    			 norm += transponedSubmatrixValues[i];
    	 }
    	 return norm;
     }
     
    /**
	 * @return  the rowIds
	 * @uml.property  name="rowIds"
	 */
    public int[] getRowIds() {
		return rowIds;
	}

    /**
	 * @param rowIds  the rowIds to set
	 * @uml.property  name="rowIds"
	 */
    public void setRowIds(int[] rowIds) {
		this.rowIds = rowIds;
	}

    /**
	 * @return  the columnIds
	 * @uml.property  name="columnIds"
	 */
    public int[] getColumnIds() {
		return columnIds;
	}

    /**
	 * @param columnIds  the columnIds to set
	 * @uml.property  name="columnIds"
	 */
    public void setColumnIds(int[] columnIds) {
		this.columnIds = columnIds;
	}

    /**
	 * @return  the values
	 * @uml.property  name="values"
	 */
    public double[] getValues() {
		return values;
	}

    /**
	 * @param values  the values to set
	 * @uml.property  name="values"
	 */
    public void setValues(double[] values) {
		this.values = values;
	}

    /**
	 * @return  the transponedSubmatrixValues
	 * @uml.property  name="transponedSubmatrixValues"
	 */
    public double[] getTransponedSubmatrixValues() {
		return transponedSubmatrixValues;
	}

    /**
	 * @param transponedSubmatrixValues  the transponedSubmatrixValues to set
	 * @uml.property  name="transponedSubmatrixValues"
	 */
    public void setTransponedSubmatrixValues(
			double[] transponedSubmatrixValues) {
				this.transponedSubmatrixValues = transponedSubmatrixValues;
			}

}
